var barcode = {
  settings: {
    width: 100,
    barWidth: 1,
    barHeight: 50,
    moduleSize: 1,
    showHRI: false,
    addQuietZone: false,
    marginHRI: 0,
    bgColor: "transparent",
    color: "#000000",
    fontSize: 12,
    output: "svg",
    posX: 0,
    posY: 0
  },
  intval: function (val) {
    var type = typeof(val);
    if (type == 'string') {
      val = val.replace(/[^0-9-.]/g, "");
      val = parseInt(val * 1, 10);
      return isNaN(val) || !isFinite(val) ? 0 : val;
    }
    return type == 'number' && isFinite(val) ? Math.floor(val) : 0;
  },
  i25: { // std25 int25
    encoding: ["NNWWN", "WNNNW", "NWNNW", "WWNNN", "NNWNW", "WNWNN", "NWWNN", "NNNWW", "WNNWN", "NWNWN"],
    compute: function (code, crc, type) {
      if (!crc) {
        if (code.length % 2 != 0) code = '0' + code;
      } else {
        if ((type == "int25") && (code.length % 2 == 0)) code = '0' + code;
        var odd = true, v, sum = 0;
        for (var i = code.length - 1; i > -1; i--) {
          v = barcode.intval(code.charAt(i));
          if (isNaN(v)) return ("");
          sum += odd ? 3 * v : v;
          odd = !odd;
        }
        code += ((10 - sum % 10) % 10).toString();
      }
      return (code);
    },
    getDigit: function (code, crc, type) {
      code = this.compute(code, crc, type);
      if (code == "") return ("");
      result = "";

      var i, j;
      if (type == "int25") {
        // Interleaved 2 of 5

        // start
        result += "1010";

        // digits + CRC
        var c1, c2;
        for (i = 0; i < code.length / 2; i++) {
          c1 = code.charAt(2 * i);
          c2 = code.charAt(2 * i + 1);
          for (j = 0; j < 5; j++) {
            result += '1';
            if (this.encoding[c1].charAt(j) == 'W') result += '1';
            result += '0';
            if (this.encoding[c2].charAt(j) == 'W') result += '0';
          }
        }
        // stop
        result += "1101";
      } else if (type == "std25") {
        // Standard 2 of 5 is a numeric-only barcode that has been in use a long time.
        // Unlike Interleaved 2 of 5, all of the information is encoded in the bars; the spaces are fixed width and are used only to separate the bars.
        // The code is self-checking and does not include a checksum.

        // start
        result += "11011010";

        // digits + CRC
        var c;
        for (var i = 0; i < code.length; i++) {
          c = code.charAt(i);
          for (j = 0; j < 5; j++) {
            result += '1';
            if (this.encoding[c].charAt(j) == 'W') result += "11";
            result += '0';
          }
        }
        // stop
        result += "11010110";
      }
      return (result);
    }
  },
  ean: {
    encoding: [["0001101", "0100111", "1110010"],
      ["0011001", "0110011", "1100110"],
      ["0010011", "0011011", "1101100"],
      ["0111101", "0100001", "1000010"],
      ["0100011", "0011101", "1011100"],
      ["0110001", "0111001", "1001110"],
      ["0101111", "0000101", "1010000"],
      ["0111011", "0010001", "1000100"],
      ["0110111", "0001001", "1001000"],
      ["0001011", "0010111", "1110100"]],
    first: ["000000", "001011", "001101", "001110", "010011", "011001", "011100", "010101", "010110", "011010"],
    getDigit: function (code, type) {
      // Check len (12 for ean13, 7 for ean8)
      var len = type == "ean8" ? 7 : 12;
      code = code.substring(0, len);
      if (code.length != len) return ("");
      // Check each digit is numeric
      var c;
      for (var i = 0; i < code.length; i++) {
        c = code.charAt(i);
        if ((c < '0') || (c > '9')) return ("");
      }
      // get checksum
      code = this.compute(code, type);

      // process analyse
      var result = "101"; // start

      if (type == "ean8") {

        // process left part
        for (var i = 0; i < 4; i++) {
          result += this.encoding[barcode.intval(code.charAt(i))][0];
        }

        // center guard bars
        result += "01010";

        // process right part
        for (var i = 4; i < 8; i++) {
          result += this.encoding[barcode.intval(code.charAt(i))][2];
        }

      } else { // ean13
        // extract first digit and get sequence
        var seq = this.first[barcode.intval(code.charAt(0))];

        // process left part
        for (var i = 1; i < 7; i++) {
          result += this.encoding[barcode.intval(code.charAt(i))][barcode.intval(seq.charAt(i - 1))];
        }

        // center guard bars
        result += "01010";

        // process right part
        for (var i = 7; i < 13; i++) {
          result += this.encoding[barcode.intval(code.charAt(i))][2];
        }
      } // ean13

      result += "101"; // stop
      return (result);
    },
    compute: function (code, type) {
      var len = type == "ean13" ? 12 : 7;
      code = code.substring(0, len);
      var sum = 0, odd = true;
      for (var i = code.length - 1; i > -1; i--) {
        sum += (odd ? 3 : 1) * barcode.intval(code.charAt(i));
        odd = !odd;
      }
      return (code + ((10 - sum % 10) % 10).toString());
    }
  },
  upc: {
    getDigit: function (code) {
      if (code.length < 12) {
        code = '0' + code;
      }
      return barcode.ean.getDigit(code, 'ean13');
    },
    compute: function (code) {
      if (code.length < 12) {
        code = '0' + code;
      }
      return barcode.ean.compute(code, 'ean13').substr(1);
    }
  },
  msi: {
    encoding: ["100100100100", "100100100110", "100100110100", "100100110110",
      "100110100100", "100110100110", "100110110100", "100110110110",
      "110100100100", "110100100110"],
    compute: function (code, crc) {
      if (typeof(crc) == "object") {
        if (crc.crc1 == "mod10") {
          code = this.computeMod10(code);
        } else if (crc.crc1 == "mod11") {
          code = this.computeMod11(code);
        }
        if (crc.crc2 == "mod10") {
          code = this.computeMod10(code);
        } else if (crc.crc2 == "mod11") {
          code = this.computeMod11(code);
        }
      } else if (typeof(crc) == "boolean") {
        if (crc) code = this.computeMod10(code);
      }
      return (code);
    },
    computeMod10: function (code) {
      var i,
        toPart1 = code.length % 2;
      var n1 = 0, sum = 0;
      for (i = 0; i < code.length; i++) {
        if (toPart1) {
          n1 = 10 * n1 + barcode.intval(code.charAt(i));
        } else {
          sum += barcode.intval(code.charAt(i));
        }
        toPart1 = !toPart1;
      }
      var s1 = (2 * n1).toString();
      for (i = 0; i < s1.length; i++) {
        sum += barcode.intval(s1.charAt(i));
      }
      return (code + ((10 - sum % 10) % 10).toString());
    },
    computeMod11: function (code) {
      var sum = 0, weight = 2;
      for (var i = code.length - 1; i >= 0; i--) {
        sum += weight * barcode.intval(code.charAt(i));
        weight = weight == 7 ? 2 : weight + 1;
      }
      return (code + ((11 - sum % 11) % 11).toString());
    },
    getDigit: function (code, crc) {
      var table = "0123456789";
      var index = 0;
      var result = "";

      code = this.compute(code, false);

      // start
      result = "110";

      // digits
      for (i = 0; i < code.length; i++) {
        index = table.indexOf(code.charAt(i));
        if (index < 0) return ("");
        result += this.encoding[index];
      }

      // stop
      result += "1001";

      return (result);
    }
  },
  code11: {
    encoding: ["101011", "1101011", "1001011", "1100101",
      "1011011", "1101101", "1001101", "1010011",
      "1101001", "110101", "101101"],
    getDigit: function (code) {
      var table = "0123456789-";
      var i, index, result = "", intercharacter = '0'

      // start
      result = "1011001" + intercharacter;

      // digits
      for (i = 0; i < code.length; i++) {
        index = table.indexOf(code.charAt(i));
        if (index < 0) return ("");
        result += this.encoding[index] + intercharacter;
      }

      // checksum
      var weightC = 0,
        weightSumC = 0,
        weightK = 1, // start at 1 because the right-most character is "C" checksum
        weightSumK = 0;
      for (i = code.length - 1; i >= 0; i--) {
        weightC = weightC == 10 ? 1 : weightC + 1;
        weightK = weightK == 10 ? 1 : weightK + 1;

        index = table.indexOf(code.charAt(i));

        weightSumC += weightC * index;
        weightSumK += weightK * index;
      }

      var c = weightSumC % 11;
      weightSumK += c;
      var k = weightSumK % 11;

      result += this.encoding[c] + intercharacter;

      if (code.length >= 10) {
        result += this.encoding[k] + intercharacter;
      }

      // stop
      result += "1011001";

      return (result);
    }
  },
  code39: {
    encoding: ["101001101101", "110100101011", "101100101011", "110110010101",
      "101001101011", "110100110101", "101100110101", "101001011011",
      "110100101101", "101100101101", "110101001011", "101101001011",
      "110110100101", "101011001011", "110101100101", "101101100101",
      "101010011011", "110101001101", "101101001101", "101011001101",
      "110101010011", "101101010011", "110110101001", "101011010011",
      "110101101001", "101101101001", "101010110011", "110101011001",
      "101101011001", "101011011001", "110010101011", "100110101011",
      "110011010101", "100101101011", "110010110101", "100110110101",
      "100101011011", "110010101101", "100110101101", "100100100101",
      "100100101001", "100101001001", "101001001001", "100101101101"],
    getDigit: function (code) {
      var table = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*";
      var i, index, result = "", intercharacter = '0';

      if (code.indexOf('*') >= 0) return ("");

      // Add Start and Stop charactere : *
      code = ("*" + code + "*").toUpperCase();

      for (i = 0; i < code.length; i++) {
        index = table.indexOf(code.charAt(i));
        if (index < 0) return ("");
        if (i > 0) result += intercharacter;
        result += this.encoding[index];
      }
      return (result);
    }
  },
  code93: {
    encoding: ["100010100", "101001000", "101000100", "101000010",
      "100101000", "100100100", "100100010", "101010000",
      "100010010", "100001010", "110101000", "110100100",
      "110100010", "110010100", "110010010", "110001010",
      "101101000", "101100100", "101100010", "100110100",
      "100011010", "101011000", "101001100", "101000110",
      "100101100", "100010110", "110110100", "110110010",
      "110101100", "110100110", "110010110", "110011010",
      "101101100", "101100110", "100110110", "100111010",
      "100101110", "111010100", "111010010", "111001010",
      "101101110", "101110110", "110101110", "100100110",
      "111011010", "111010110", "100110010", "101011110"],
    getDigit: function (code, crc) {
      var table = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%____*", // _ => ($), (%), (/) et (+)
        c, result = "";

      if (code.indexOf('*') >= 0) return ("");

      code = code.toUpperCase();

      // start :  *
      result += this.encoding[47];

      // digits
      for (i = 0; i < code.length; i++) {
        c = code.charAt(i);
        index = table.indexOf(c);
        if ((c == '_') || (index < 0)) return ("");
        result += this.encoding[index];
      }

      // checksum
      if (crc) {
        var weightC = 0,
          weightSumC = 0,
          weightK = 1, // start at 1 because the right-most character is "C" checksum
          weightSumK = 0;
        for (i = code.length - 1; i >= 0; i--) {
          weightC = weightC == 20 ? 1 : weightC + 1;
          weightK = weightK == 15 ? 1 : weightK + 1;

          index = table.indexOf(code.charAt(i));

          weightSumC += weightC * index;
          weightSumK += weightK * index;
        }

        var c = weightSumC % 47;
        weightSumK += c;
        var k = weightSumK % 47;

        result += this.encoding[c];
        result += this.encoding[k];
      }

      // stop : *
      result += this.encoding[47];

      // Terminaison bar
      result += '1';
      return (result);
    }
  },
  code128: {
    encoding: ["11011001100", "11001101100", "11001100110", "10010011000",
      "10010001100", "10001001100", "10011001000", "10011000100",
      "10001100100", "11001001000", "11001000100", "11000100100",
      "10110011100", "10011011100", "10011001110", "10111001100",
      "10011101100", "10011100110", "11001110010", "11001011100",
      "11001001110", "11011100100", "11001110100", "11101101110",
      "11101001100", "11100101100", "11100100110", "11101100100",
      "11100110100", "11100110010", "11011011000", "11011000110",
      "11000110110", "10100011000", "10001011000", "10001000110",
      "10110001000", "10001101000", "10001100010", "11010001000",
      "11000101000", "11000100010", "10110111000", "10110001110",
      "10001101110", "10111011000", "10111000110", "10001110110",
      "11101110110", "11010001110", "11000101110", "11011101000",
      "11011100010", "11011101110", "11101011000", "11101000110",
      "11100010110", "11101101000", "11101100010", "11100011010",
      "11101111010", "11001000010", "11110001010", "10100110000",
      "10100001100", "10010110000", "10010000110", "10000101100",
      "10000100110", "10110010000", "10110000100", "10011010000",
      "10011000010", "10000110100", "10000110010", "11000010010",
      "11001010000", "11110111010", "11000010100", "10001111010",
      "10100111100", "10010111100", "10010011110", "10111100100",
      "10011110100", "10011110010", "11110100100", "11110010100",
      "11110010010", "11011011110", "11011110110", "11110110110",
      "10101111000", "10100011110", "10001011110", "10111101000",
      "10111100010", "11110101000", "11110100010", "10111011110",
      "10111101110", "11101011110", "11110101110", "11010000100",
      "11010010000", "11010011100", "11000111010"],
    getDigit: function (code) {
      var tableB = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
      var result = "";
      var sum = 0;
      var isum = 0;
      var i = 0;
      var j = 0;
      var value = 0;

      // check each characters
      for (i = 0; i < code.length; i++) {
        if (tableB.indexOf(code.charAt(i)) == -1) return ("");
      }

      // check firsts characters : start with C table only if enought numeric
      var tableCActivated = code.length > 1;
      var c = '';
      for (i = 0; i < 3 && i < code.length; i++) {
        c = code.charAt(i);
        tableCActivated &= c >= '0' && c <= '9';
      }

      sum = tableCActivated ? 105 : 104;

      // start : [105] : C table or [104] : B table
      result = this.encoding[sum];

      i = 0;
      while (i < code.length) {
        if (!tableCActivated) {
          j = 0;
          // check next character to activate C table if interresting
          while ((i + j < code.length) && (code.charAt(i + j) >= '0') && (code.charAt(i + j) <= '9')) j++;

          // 6 min everywhere or 4 mini at the end
          tableCActivated = (j > 5) || ((i + j - 1 == code.length) && (j > 3));

          if (tableCActivated) {
            result += this.encoding[99]; // C table
            sum += ++isum * 99;
          }
          //         2 min for table C so need table B
        } else if ((i == code.length) || (code.charAt(i) < '0') || (code.charAt(i) > '9') || (code.charAt(i + 1) < '0') || (code.charAt(i + 1) > '9')) {
          tableCActivated = false;
          result += this.encoding[100]; // B table
          sum += ++isum * 100;
        }

        if (tableCActivated) {
          value = barcode.intval(code.charAt(i) + code.charAt(i + 1)); // Add two characters (numeric)
          i += 2;
        } else {
          value = tableB.indexOf(code.charAt(i)); // Add one character
          i += 1;
        }
        result += this.encoding[value];
        sum += ++isum * value;
      }

      // Add CRC
      result += this.encoding[sum % 103];

      // Stop
      result += this.encoding[106];

      // Termination bar
      result += "11";

      return (result);
    }
  },
  codabar: {
    encoding: ["101010011", "101011001", "101001011", "110010101",
      "101101001", "110101001", "100101011", "100101101",
      "100110101", "110100101", "101001101", "101100101",
      "1101011011", "1101101011", "1101101101", "1011011011",
      "1011001001", "1010010011", "1001001011", "1010011001"],
    getDigit: function (code) {
      var table = "0123456789-$:/.+";
      var i, index, result = "", intercharacter = '0';

      // add start : A->D : arbitrary choose A
      result += this.encoding[16] + intercharacter;

      for (i = 0; i < code.length; i++) {
        index = table.indexOf(code.charAt(i));
        if (index < 0) return ("");
        result += this.encoding[index] + intercharacter;
      }

      // add stop : A->D : arbitrary choose A
      result += this.encoding[16];
      return (result);
    }
  },
  datamatrix: {
    lengthRows: [10, 12, 14, 16, 18, 20, 22, 24, 26,  // 24 squares et 6 rectangular
      32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
      8, 8, 12, 12, 16, 16],
    lengthCols: [10, 12, 14, 16, 18, 20, 22, 24, 26,  // Number of columns for the entire datamatrix
      32, 36, 40, 44, 48, 52, 64, 72, 80, 88, 96, 104, 120, 132, 144,
      18, 32, 26, 36, 36, 48],
    dataCWCount: [3, 5, 8, 12, 18, 22, 30, 36,  // Number of data codewords for the datamatrix
      44, 62, 86, 114, 144, 174, 204, 280, 368, 456, 576, 696, 816, 1050,
      1304, 1558, 5, 10, 16, 22, 32, 49],
    solomonCWCount: [5, 7, 10, 12, 14, 18, 20, 24, 28, // Number of Reed-Solomon codewords for the datamatrix
      36, 42, 48, 56, 68, 84, 112, 144, 192, 224, 272, 336, 408, 496, 620,
      7, 11, 14, 18, 24, 28],
    dataRegionRows: [8, 10, 12, 14, 16, 18, 20, 22, // Number of rows per region
      24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
      6, 6, 10, 10, 14, 14],
    dataRegionCols: [8, 10, 12, 14, 16, 18, 20, 22, // Number of columns per region
      24, 14, 16, 18, 20, 22, 24, 14, 16, 18, 20, 22, 24, 18, 20, 22,
      16, 14, 24, 16, 16, 22],
    regionRows: [1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per row
      1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
      1, 1, 1, 1, 1, 1],
    regionCols: [1, 1, 1, 1, 1, 1, 1, 1, // Number of regions per column
      1, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 6, 6, 6,
      1, 2, 1, 2, 2, 2],
    interleavedBlocks: [1, 1, 1, 1, 1, 1, 1, 1, // Number of blocks
      1, 1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 6, 6, 8, 8,
      1, 1, 1, 1, 1, 1],
    logTab: [-255, 255, 1, 240, 2, 225, 241, 53, 3,  // Table of log for the Galois field
      38, 226, 133, 242, 43, 54, 210, 4, 195, 39, 114, 227, 106, 134, 28,
      243, 140, 44, 23, 55, 118, 211, 234, 5, 219, 196, 96, 40, 222, 115,
      103, 228, 78, 107, 125, 135, 8, 29, 162, 244, 186, 141, 180, 45, 99,
      24, 49, 56, 13, 119, 153, 212, 199, 235, 91, 6, 76, 220, 217, 197,
      11, 97, 184, 41, 36, 223, 253, 116, 138, 104, 193, 229, 86, 79, 171,
      108, 165, 126, 145, 136, 34, 9, 74, 30, 32, 163, 84, 245, 173, 187,
      204, 142, 81, 181, 190, 46, 88, 100, 159, 25, 231, 50, 207, 57, 147,
      14, 67, 120, 128, 154, 248, 213, 167, 200, 63, 236, 110, 92, 176, 7,
      161, 77, 124, 221, 102, 218, 95, 198, 90, 12, 152, 98, 48, 185, 179,
      42, 209, 37, 132, 224, 52, 254, 239, 117, 233, 139, 22, 105, 27, 194,
      113, 230, 206, 87, 158, 80, 189, 172, 203, 109, 175, 166, 62, 127,
      247, 146, 66, 137, 192, 35, 252, 10, 183, 75, 216, 31, 83, 33, 73,
      164, 144, 85, 170, 246, 65, 174, 61, 188, 202, 205, 157, 143, 169, 82,
      72, 182, 215, 191, 251, 47, 178, 89, 151, 101, 94, 160, 123, 26, 112,
      232, 21, 51, 238, 208, 131, 58, 69, 148, 18, 15, 16, 68, 17, 121, 149,
      129, 19, 155, 59, 249, 70, 214, 250, 168, 71, 201, 156, 64, 60, 237,
      130, 111, 20, 93, 122, 177, 150],
    aLogTab: [1, 2, 4, 8, 16, 32, 64, 128, 45, 90, // Table of aLog for the Galois field
      180, 69, 138, 57, 114, 228, 229, 231, 227, 235, 251, 219, 155, 27, 54,
      108, 216, 157, 23, 46, 92, 184, 93, 186, 89, 178, 73, 146, 9, 18, 36,
      72, 144, 13, 26, 52, 104, 208, 141, 55, 110, 220, 149, 7, 14, 28, 56,
      112, 224, 237, 247, 195, 171, 123, 246, 193, 175, 115, 230, 225, 239,
      243, 203, 187, 91, 182, 65, 130, 41, 82, 164, 101, 202, 185, 95, 190,
      81, 162, 105, 210, 137, 63, 126, 252, 213, 135, 35, 70, 140, 53, 106,
      212, 133, 39, 78, 156, 21, 42, 84, 168, 125, 250, 217, 159, 19, 38, 76,
      152, 29, 58, 116, 232, 253, 215, 131, 43, 86, 172, 117, 234, 249, 223,
      147, 11, 22, 44, 88, 176, 77, 154, 25, 50, 100, 200, 189, 87, 174, 113,
      226, 233, 255, 211, 139, 59, 118, 236, 245, 199, 163, 107, 214, 129,
      47, 94, 188, 85, 170, 121, 242, 201, 191, 83, 166, 97, 194, 169, 127,
      254, 209, 143, 51, 102, 204, 181, 71, 142, 49, 98, 196, 165, 103, 206,
      177, 79, 158, 17, 34, 68, 136, 61, 122, 244, 197, 167, 99, 198, 161,
      111, 222, 145, 15, 30, 60, 120, 240, 205, 183, 67, 134, 33, 66, 132,
      37, 74, 148, 5, 10, 20, 40, 80, 160, 109, 218, 153, 31, 62, 124, 248,
      221, 151, 3, 6, 12, 24, 48, 96, 192, 173, 119, 238, 241, 207, 179, 75,
      150, 1],
    champGaloisMult: function (a, b) {  // MULTIPLICATION IN GALOIS FIELD GF(2^8)
      if (!a || !b) return 0;
      return this.aLogTab[(this.logTab[a] + this.logTab[b]) % 255];
    },
    champGaloisDoub: function (a, b) {  // THE OPERATION a * 2^b IN GALOIS FIELD GF(2^8)
      if (!a) return 0;
      if (!b) return a;
      return this.aLogTab[(this.logTab[a] + b) % 255];
    },
    champGaloisSum: function (a, b) { // SUM IN GALOIS FIELD GF(2^8)
      return a ^ b;
    },
    selectIndex: function (dataCodeWordsCount, rectangular) { // CHOOSE THE GOOD INDEX FOR TABLES
      if ((dataCodeWordsCount < 1 || dataCodeWordsCount > 1558) && !rectangular) return -1;
      if ((dataCodeWordsCount < 1 || dataCodeWordsCount > 49) && rectangular) return -1;

      var n = 0;
      if (rectangular) n = 24;

      while (this.dataCWCount[n] < dataCodeWordsCount) n++;
      return n;
    },
    encodeDataCodeWordsASCII: function (text) {
      var dataCodeWords = new Array();
      var n = 0, i, c;
      for (i = 0; i < text.length; i++) {
        c = text.charCodeAt(i);
        if (c > 127) {
          dataCodeWords[n] = 235;
          c = c - 127;
          n++;
        } else if ((c >= 48 && c <= 57) && (i + 1 < text.length) && (text.charCodeAt(i + 1) >= 48 && text.charCodeAt(i + 1) <= 57)) {
          c = ((c - 48) * 10) + ((text.charCodeAt(i + 1)) - 48);
          c += 130;
          i++;
        } else c++;
        dataCodeWords[n] = c;
        n++;
      }
      return dataCodeWords;
    },
    addPadCW: function (tab, from, to) {
      if (from >= to) return;
      tab[from] = 129;
      var r, i;
      for (i = from + 1; i < to; i++) {
        r = ((149 * (i + 1)) % 253) + 1;
        tab[i] = (129 + r) % 254;
      }
    },
    calculSolFactorTable: function (solomonCWCount) { // CALCULATE THE REED SOLOMON FACTORS
      var g = new Array();
      var i, j;

      for (i = 0; i <= solomonCWCount; i++) g[i] = 1;

      for (i = 1; i <= solomonCWCount; i++) {
        for (j = i - 1; j >= 0; j--) {
          g[j] = this.champGaloisDoub(g[j], i);
          if (j > 0) g[j] = this.champGaloisSum(g[j], g[j - 1]);
        }
      }
      return g;
    },
    addReedSolomonCW: function (nSolomonCW, coeffTab, nDataCW, dataTab, blocks) { // Add the Reed Solomon codewords
      var temp = 0;
      var errorBlocks = nSolomonCW / blocks;
      var correctionCW = new Array();

      var i, j, k;
      for (k = 0; k < blocks; k++) {
        for (i = 0; i < errorBlocks; i++) correctionCW[i] = 0;

        for (i = k; i < nDataCW; i = i + blocks) {
          temp = this.champGaloisSum(dataTab[i], correctionCW[errorBlocks - 1]);
          for (j = errorBlocks - 1; j >= 0; j--) {
            if (!temp) {
              correctionCW[j] = 0;
            } else {
              correctionCW[j] = this.champGaloisMult(temp, coeffTab[j]);
            }
            if (j > 0) correctionCW[j] = this.champGaloisSum(correctionCW[j - 1], correctionCW[j]);
          }
        }
        // Renversement des blocs calcules
        j = nDataCW + k;
        for (i = errorBlocks - 1; i >= 0; i--) {
          dataTab[j] = correctionCW[i];
          j = j + blocks;
        }
      }
      return dataTab;
    },
    getBits: function (entier) { // Transform integer to tab of bits
      var bits = new Array();
      for (var i = 0; i < 8; i++) {
        bits[i] = entier & (128 >> i) ? 1 : 0;
      }
      return bits;
    },
    next: function (etape, totalRows, totalCols, codeWordsBits, datamatrix, assigned) { // Place codewords into the matrix
      var chr = 0; // Place of the 8st bit from the first character to [4][0]
      var row = 4;
      var col = 0;

      do {
        // Check for a special case of corner
        if ((row == totalRows) && (col == 0)) {
          this.patternShapeSpecial1(datamatrix, assigned, codeWordsBits[chr], totalRows, totalCols);
          chr++;
        } else if ((etape < 3) && (row == totalRows - 2) && (col == 0) && (totalCols % 4 != 0)) {
          this.patternShapeSpecial2(datamatrix, assigned, codeWordsBits[chr], totalRows, totalCols);
          chr++;
        } else if ((row == totalRows - 2) && (col == 0) && (totalCols % 8 == 4)) {
          this.patternShapeSpecial3(datamatrix, assigned, codeWordsBits[chr], totalRows, totalCols);
          chr++;
        }
        else if ((row == totalRows + 4) && (col == 2) && (totalCols % 8 == 0)) {
          this.patternShapeSpecial4(datamatrix, assigned, codeWordsBits[chr], totalRows, totalCols);
          chr++;
        }

        // Go up and right in the datamatrix
        do {
          if ((row < totalRows) && (col >= 0) && (assigned[row][col] != 1)) {
            this.patternShapeStandard(datamatrix, assigned, codeWordsBits[chr], row, col, totalRows, totalCols);
            chr++;
          }
          row -= 2;
          col += 2;
        } while ((row >= 0) && (col < totalCols));
        row += 1;
        col += 3;

        // Go down and left in the datamatrix
        do {
          if ((row >= 0) && (col < totalCols) && (assigned[row][col] != 1)) {
            this.patternShapeStandard(datamatrix, assigned, codeWordsBits[chr], row, col, totalRows, totalCols);
            chr++;
          }
          row += 2;
          col -= 2;
        } while ((row < totalRows) && (col >= 0));
        row += 3;
        col += 1;
      } while ((row < totalRows) || (col < totalCols));
    },
    patternShapeStandard: function (datamatrix, assigned, bits, row, col, totalRows, totalCols) { // Place bits in the matrix (standard or special case)
      this.placeBitInDatamatrix(datamatrix, assigned, bits[0], row - 2, col - 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[1], row - 2, col - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[2], row - 1, col - 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[3], row - 1, col - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[4], row - 1, col, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[5], row, col - 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[6], row, col - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[7], row, col, totalRows, totalCols);
    },
    patternShapeSpecial1: function (datamatrix, assigned, bits, totalRows, totalCols) {
      this.placeBitInDatamatrix(datamatrix, assigned, bits[0], totalRows - 1, 0, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[1], totalRows - 1, 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[2], totalRows - 1, 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[3], 0, totalCols - 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[4], 0, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[5], 1, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[6], 2, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[7], 3, totalCols - 1, totalRows, totalCols);
    },
    patternShapeSpecial2: function (datamatrix, assigned, bits, totalRows, totalCols) {
      this.placeBitInDatamatrix(datamatrix, assigned, bits[0], totalRows - 3, 0, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[1], totalRows - 2, 0, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[2], totalRows - 1, 0, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[3], 0, totalCols - 4, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[4], 0, totalCols - 3, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[5], 0, totalCols - 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[6], 0, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[7], 1, totalCols - 1, totalRows, totalCols);
    },
    patternShapeSpecial3: function (datamatrix, assigned, bits, totalRows, totalCols) {
      this.placeBitInDatamatrix(datamatrix, assigned, bits[0], totalRows - 3, 0, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[1], totalRows - 2, 0, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[2], totalRows - 1, 0, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[3], 0, totalCols - 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[4], 0, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[5], 1, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[6], 2, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[7], 3, totalCols - 1, totalRows, totalCols);
    },
    patternShapeSpecial4: function (datamatrix, assigned, bits, totalRows, totalCols) {
      this.placeBitInDatamatrix(datamatrix, assigned, bits[0], totalRows - 1, 0, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[1], totalRows - 1, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[2], 0, totalCols - 3, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[3], 0, totalCols - 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[4], 0, totalCols - 1, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[5], 1, totalCols - 3, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[6], 1, totalCols - 2, totalRows, totalCols);
      this.placeBitInDatamatrix(datamatrix, assigned, bits[7], 1, totalCols - 1, totalRows, totalCols);
    },
    placeBitInDatamatrix: function (datamatrix, assigned, bit, row, col, totalRows, totalCols) { // Put a bit into the matrix
      if (row < 0) {
        row += totalRows;
        col += 4 - ((totalRows + 4) % 8);
      }
      if (col < 0) {
        col += totalCols;
        row += 4 - ((totalCols + 4) % 8);
      }
      if (assigned[row][col] != 1) {
        datamatrix[row][col] = bit;
        assigned[row][col] = 1;
      }
    },
    addFinderPattern: function (datamatrix, rowsRegion, colsRegion, rowsRegionCW, colsRegionCW) { // Add the finder pattern
      var totalRowsCW = (rowsRegionCW + 2) * rowsRegion;
      var totalColsCW = (colsRegionCW + 2) * colsRegion;

      var datamatrixTemp = new Array();
      datamatrixTemp[0] = new Array();
      for (var j = 0; j < totalColsCW + 2; j++) {
        datamatrixTemp[0][j] = 0;
      }
      for (var i = 0; i < totalRowsCW; i++) {
        datamatrixTemp[i + 1] = new Array();
        datamatrixTemp[i + 1][0] = 0;
        datamatrixTemp[i + 1][totalColsCW + 1] = 0;
        for (var j = 0; j < totalColsCW; j++) {
          if (i % (rowsRegionCW + 2) == 0) {
            if (j % 2 == 0) {
              datamatrixTemp[i + 1][j + 1] = 1;
            } else {
              datamatrixTemp[i + 1][j + 1] = 0;
            }
          } else if (i % (rowsRegionCW + 2) == rowsRegionCW + 1) {
            datamatrixTemp[i + 1][j + 1] = 1;
          } else if (j % (colsRegionCW + 2) == colsRegionCW + 1) {
            if (i % 2 == 0) {
              datamatrixTemp[i + 1][j + 1] = 0;
            } else {
              datamatrixTemp[i + 1][j + 1] = 1;
            }
          } else if (j % (colsRegionCW + 2) == 0) {
            datamatrixTemp[i + 1][j + 1] = 1;
          } else {
            datamatrixTemp[i + 1][j + 1] = 0;
            datamatrixTemp[i + 1][j + 1] = datamatrix[i - 1 - (2 * (parseInt(i / (rowsRegionCW + 2))))][j - 1 - (2 * (parseInt(j / (colsRegionCW + 2))))];
          }
        }
      }
      datamatrixTemp[totalRowsCW + 1] = new Array();
      for (var j = 0; j < totalColsCW + 2; j++) {
        datamatrixTemp[totalRowsCW + 1][j] = 0;
      }
      return datamatrixTemp;
    },
    getDigit: function (text, rectangular) {
      var dataCodeWords = this.encodeDataCodeWordsASCII(text); // Code the text in the ASCII mode
      var dataCWCount = dataCodeWords.length;
      var index = this.selectIndex(dataCWCount, rectangular); // Select the index for the data tables
      var totalDataCWCount = this.dataCWCount[index]; // Number of data CW
      var solomonCWCount = this.solomonCWCount[index]; // Number of Reed Solomon CW
      var totalCWCount = totalDataCWCount + solomonCWCount; // Number of CW
      var rowsTotal = this.lengthRows[index]; // Size of symbol
      var colsTotal = this.lengthCols[index];
      var rowsRegion = this.regionRows[index]; // Number of region
      var colsRegion = this.regionCols[index];
      var rowsRegionCW = this.dataRegionRows[index];
      var colsRegionCW = this.dataRegionCols[index];
      var rowsLengthMatrice = rowsTotal - 2 * rowsRegion; // Size of matrice data
      var colsLengthMatrice = colsTotal - 2 * colsRegion;
      var blocks = this.interleavedBlocks[index];  // Number of Reed Solomon blocks
      var errorBlocks = (solomonCWCount / blocks);

      this.addPadCW(dataCodeWords, dataCWCount, totalDataCWCount); // Add codewords pads

      var g = this.calculSolFactorTable(errorBlocks); // Calculate correction coefficients

      this.addReedSolomonCW(solomonCWCount, g, totalDataCWCount, dataCodeWords, blocks); // Add Reed Solomon codewords

      var codeWordsBits = new Array(); // Calculte bits from codewords
      for (var i = 0; i < totalCWCount; i++) {
        codeWordsBits[i] = this.getBits(dataCodeWords[i]);
      }

      var datamatrix = new Array(); // Put data in the matrix
      var assigned = new Array();

      for (var i = 0; i < colsLengthMatrice; i++) {
        datamatrix[i] = new Array();
        assigned[i] = new Array();
      }

      // Add the bottom-right corner if needed
      if (((rowsLengthMatrice * colsLengthMatrice) % 8) == 4) {
        datamatrix[rowsLengthMatrice - 2][colsLengthMatrice - 2] = 1;
        datamatrix[rowsLengthMatrice - 1][colsLengthMatrice - 1] = 1;
        datamatrix[rowsLengthMatrice - 1][colsLengthMatrice - 2] = 0;
        datamatrix[rowsLengthMatrice - 2][colsLengthMatrice - 1] = 0;
        assigned[rowsLengthMatrice - 2][colsLengthMatrice - 2] = 1;
        assigned[rowsLengthMatrice - 1][colsLengthMatrice - 1] = 1;
        assigned[rowsLengthMatrice - 1][colsLengthMatrice - 2] = 1;
        assigned[rowsLengthMatrice - 2][colsLengthMatrice - 1] = 1;
      }

      // Put the codewords into the matrix
      this.next(0, rowsLengthMatrice, colsLengthMatrice, codeWordsBits, datamatrix, assigned);

      // Add the finder pattern
      datamatrix = this.addFinderPattern(datamatrix, rowsRegion, colsRegion, rowsRegionCW, colsRegionCW);

      return datamatrix;
    }
  },
  // little endian convertor
  lec: {
    // convert an int
    cInt: function (value, byteCount) {
      var le = '';
      for (var i = 0; i < byteCount; i++) {
        le += String.fromCharCode(value & 0xFF);
        value = value >> 8;
      }
      return le;
    },
    // return a byte string from rgb values
    cRgb: function (r, g, b) {
      return String.fromCharCode(b) + String.fromCharCode(g) + String.fromCharCode(r);
    },
    // return a byte string from a hex string color
    cHexColor: function (hex) {
      var v = parseInt('0x' + hex.substr(1));
      var b = v & 0xFF;
      v = v >> 8;
      var g = v & 0xFF;
      var r = v >> 8;
      return (this.cRgb(r, g, b));
    }
  },
  // convert a bit string to an array of array of bit char
  bitStringTo2DArray: function (digit) {
    var d = [];
    d[0] = [];
    for (var i = 0; i < digit.length; i++) d[0][i] = digit.charAt(i);
    return (d);
  },

  // svg barcode renderer
  digitToSvgRenderer: function (settings, digit, hri, callback, mwi, mhi) {
    var lines = digit.length;
    var columns = digit[0].length;

    var width = settings.width;
    var mw = width / columns;
    var mh = mhi;
    var height = mh * lines;
    if (settings.showHRI) {
      var fontSize = barcode.intval(settings.fontSize);
      height += barcode.intval(settings.marginHRI) + fontSize;
    }
    // svg header
    var svg = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 ' + width + ' ' + height + '">';
    if (settings.bgColor !== 'transparent') {
      // background
      svg += '<rect width="' + width + '" height="' + height + '" x="0" y="0" fill="' + settings.bgColor + '"/>';
    }

    var bar1 = '<rect width="&W" height="' + mh + '" x="&X" y="&Y" fill="' + settings.color + '"/>';
    if (settings.color === "#000000") {
      bar1 = '<rect width="&W" height="' + mh + '" x="&X" y="&Y"/>';
    }
    var len, current;
    for (var y = 0; y < lines; y++) {
      len = 0;
      current = digit[y][0];
      for (var x = 0; x < columns; x++) {
        if (current == digit[y][x]) {
          len++;
        } else {
          if (current == '1') {
            svg += bar1.replace("&W", (len * mw).toFixed(1))
              .replace("&X", ((x - len) * mw).toFixed(1))
              .replace("&Y", y * mh);
          }
          current = digit[y][x];
          len = 1;
        }
      }
      if ((len > 0) && (current == '1')) {
        svg += bar1.replace("&W", (len * mw).toFixed(1))
          .replace("&X", ((columns - len) * mw).toFixed(1))
          .replace("&Y", (y * mh).toFixed(1));
      }
    }

    if (settings.showHRI) {
      svg += '<text transform="translate(50 0)" y="60" text-anchor="middle">' + hri + '</text>';
    }
    // svg footer
    svg += '</svg>';
    return svg;
  },
  // svg 1D barcode renderer
  digitToSvg: function (settings, digit, hri, callback) {
    var w = barcode.intval(settings.barWidth);
    var h = barcode.intval(settings.barHeight);
    return this.digitToSvgRenderer(settings, this.bitStringTo2DArray(digit), hri, callback, w, h);
  },
  // svg 2D barcode renderer
  digitToSvg2D: function (settings, digit, hri, callback) {
    var s = barcode.intval(settings.moduleSize);
    return this.digitToSvgRenderer(settings, digit, hri, callback, s, s);
  },

};

module.exports = function (datas, type, settings, callback) {
  var digit = "",
    hri = "",
    code = "",
    crc = true,
    rect = false,
    b2d = false;

  if (typeof(datas) == "string") {
    code = datas;
  } else if (typeof(datas) == "object") {
    code = typeof(datas.code) == "string" ? datas.code : "";
    crc = typeof(datas.crc) != "undefined" ? datas.crc : true;
    rect = typeof(datas.rect) != "undefined" ? datas.rect : false;
  }
  if (code == "") return (false);

  if (typeof(settings) == "undefined") settings = [];
  for (var name in barcode.settings) {
    if (settings[name] == undefined) settings[name] = barcode.settings[name];
  }

  switch (type) {
    case "std25":
    case "int25":
      digit = barcode.i25.getDigit(code, crc, type);
      hri = barcode.i25.compute(code, crc, type);
      break;
    case "ean8":
    case "ean13":
      digit = barcode.ean.getDigit(code, type);
      hri = barcode.ean.compute(code, type);
      break;
    case "upc":
      digit = barcode.upc.getDigit(code);
      hri = barcode.upc.compute(code);
      break;
    case "code11":
      digit = barcode.code11.getDigit(code);
      hri = code;
      break;
    case "code39":
      digit = barcode.code39.getDigit(code);
      hri = code;
      break;
    case "code93":
      digit = barcode.code93.getDigit(code, crc);
      hri = code;
      break;
    case "code128":
      digit = barcode.code128.getDigit(code);
      hri = code;
      break;
    case "codabar":
      digit = barcode.codabar.getDigit(code);
      hri = code;
      break;
    case "msi":
      digit = barcode.msi.getDigit(code, crc);
      hri = barcode.msi.compute(code, crc);
      break;
    case "datamatrix":
      digit = barcode.datamatrix.getDigit(code, rect);
      hri = code;
      b2d = true;
      break;
  }
  if (digit.length == 0) return false;

  // Quiet Zone
  if (!b2d && settings.addQuietZone) digit = "0000000000" + digit + "0000000000";

  var fname = 'digitTo' + settings.output.charAt(0).toUpperCase() + settings.output.substr(1) + (b2d ? '2D' : '');
  //if (typeof(barcode[fname]) == 'function' && settings.toFile === false) {
  //    return barcode[fname](settings, digit, hri, callback);
  //}else
  if (typeof(barcode[fname]) == 'function') {
    return barcode[fname](settings, digit, hri, callback);
  }
};
